package com.tjbknb.ecui;

import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Scanner;

import android.app.Notification;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.IBinder;
import android.os.Looper;
import android.util.Log;
import android.widget.Toast;


public class TestService extends Service{
	static Data data;
	private static final String TAG = "Service";
	int ALGORITHM = 2;
	Thread t;


	@Override
	public IBinder onBind(Intent arg0) {
		// TODO Auto-generated method stub
		return null;
	}


	@Override
	public void onCreate() 
	{
		//code to execute when the service is first created


		Toast.makeText(getApplicationContext(), "service created", Toast.LENGTH_SHORT).show();
	}

	@Override
	public void onDestroy() 
	{
		//code to execute when the service is shutting down
		t = null;
		executeSu("echo ondemand > /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor");
		//System.exit(0);
		Toast.makeText(getApplicationContext(), "service destroyed", Toast.LENGTH_SHORT).show();
	}

	@Override
	public int onStartCommand(Intent intent, int flags, int startid) 
	{
		//code to execute when the service is starting up
		Notification notification = new Notification(R.drawable.icon, "ECUI",
				System.currentTimeMillis());
		Intent notificationIntent = new Intent(this, ECUIActivity.class);
		notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP|
				Intent.FLAG_ACTIVITY_SINGLE_TOP);
		PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
		notification.setLatestEventInfo(this, "ECUI", "Currently running", pendingIntent);
		notification.flags|=Notification.FLAG_NO_CLEAR;
		startForeground(1337, notification);


		Toast.makeText(getApplicationContext(), "service started", Toast.LENGTH_SHORT).show();

		data = new Data();

		//getRoot(getApplicationContext());

		setGovernor();
		String temp = setMaxFreq();
		Toast.makeText(getApplicationContext(), temp, Toast.LENGTH_SHORT).show();

		if(ALGORITHM == 1){
			t = new Thread(){
				@Override
				public void run() 
				{

					Log.d(TAG, "thread started");


					//this.setDaemon(true);
					int count = 0;

					Thread thisThread = Thread.currentThread();

					Looper.prepare();
					while (t == thisThread)
					{
						if(count == 15){
							decreaseFreq();
							count = 0;
							Log.d(TAG, "decreasing");
							Toast.makeText(getApplicationContext(), "freq decreased", Toast.LENGTH_LONG).show();
						}
						else if(count == 5){
							Log.d(TAG, "5");
							Toast.makeText(getApplicationContext(), "5", Toast.LENGTH_SHORT).show();
						}
						else if(count == 10){
							Log.d(TAG, "10");
							Toast.makeText(getApplicationContext(), "10", Toast.LENGTH_SHORT).show();
						}

						//pause 
						try 
						{
							Thread.sleep(1000);
						} 
						catch (InterruptedException e) 
						{
							// TODO Auto-generated catch block
							e.printStackTrace();
						}

						count++;
					}


				}

			};

			t.start();
		}
		else if(ALGORITHM == 2){
			t = new Thread(){
				@Override
				public void run() 
				{
					//t.setDaemon(true);
					int count = 0;
					int curr_freq = Integer.parseInt(data.getMaxFreq());
					String[] freqdata = calculateFreq(curr_freq);
					double time = 1000 * Double.parseDouble(freqdata[0]);

					Looper.prepare();
					
					Thread thisThread = Thread.currentThread();
					
					while (t == thisThread)
					{
						if(count == 15){
							Log.d(TAG, "decreasing");
							curr_freq = Integer.parseInt(getCurrentFreq()) - 100000;
							freqdata = calculateFreq(curr_freq);
							time = 15000 * Double.parseDouble(freqdata[0]);
							count = 0;
							Toast.makeText(getApplicationContext(), "freq decreased", Toast.LENGTH_LONG).show();
						}

						if(freqdata[0].equals("1")){
							Log.d(TAG, "freqdata[0].equals(\"1\")");
							executeSu("echo " + freqdata[1] + " > /sys/devices/system/cpu/cpu0/cpufreq/scaling_setspeed");
							try {
								Thread.sleep(15000);
							} catch (InterruptedException e) {
								e.printStackTrace();
							}
							count = 15;
						}
						else{
							Log.d(TAG, "alternate");
							executeSu("echo " + freqdata[1] + " > /sys/devices/system/cpu/cpu0/cpufreq/scaling_setspeed");

							try {
								Thread.sleep((long) time);
							} catch (InterruptedException e) {
								e.printStackTrace();
							}

							executeSu("echo " + freqdata[2] + " > /sys/devices/system/cpu/cpu0/cpufreq/scaling_setspeed");

							try {
								Thread.sleep(15000 - (long) time);
							} catch (InterruptedException e) {
								e.printStackTrace();
							}
							count = 15;
						}



					}
				}
			};

			t.start();
		}
		else if(ALGORITHM ==3){

		}



		return (START_NOT_STICKY);
	}



	//run when user is impatient
	public static void userIsImpatient() throws IOException{
		setMaxFreq();
	}

	//get superuser permission
	public static void getRoot(Context c)
	{
		Process p;  
		try {  
			// Perform su to get root privledges  
			p = Runtime.getRuntime().exec("su");   

			// Attempt to write a file to a root-only  
			DataOutputStream os = new DataOutputStream(p.getOutputStream());  
			os.writeBytes("echo \"Do I have root?\" >/system/sd/temporary.txt\n");  

			// Close the terminal  
			os.writeBytes("exit\n");  
			os.flush();  
			try {  
				p.waitFor();  
				if (p.exitValue() != 255) {  
					// TODO Code to run on success  
					Toast.makeText(c, "root", Toast.LENGTH_SHORT).show();  
				}  
				else {  
					// TODO Code to run on unsuccessful  
					Toast.makeText(c, "not root", Toast.LENGTH_SHORT).show();  
				}  
			} catch (InterruptedException e) {  
				// TODO Code to run in interrupted exception  
				Toast.makeText(c, "not root", Toast.LENGTH_SHORT).show();  
			}  
		} catch (IOException e) {  
			// TODO Code to run in input/output exception  
			Toast.makeText(c, "not root", Toast.LENGTH_SHORT).show();  
		}  

	}

	//returns current frequency in KHz
	public static String getCurrentFreq()
	{	
		String result = executeRead("/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq");

		if (result.length() == 0)
		{
			return Long.toString(System.nanoTime());
		}
		else
		{
			return result;
		}

	}	

	//returns available frequencies
	public static String getAvailableFreq()
	{
		return executeRead("/sys/devices/system/cpu/cpu0/cpufreq/scaling_available_frequencies");
	}

	//returns available governors
	public static String getGovernors()
	{
		String ret = executeRead("/sys/devices/system/cpu/cpu0/cpufreq/scaling_available_governors");
		return ret;
	}


	//set governor to userspace
	public static void setGovernor()
	{
		executeSu("echo userspace > /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor");

	}


	//set the CPU to maximum frequency
	public static String setMaxFreq()
	{	
		executeSu("echo " + data.getMaxFreq() + " > /sys/devices/system/cpu/cpu0/cpufreq/scaling_setspeed");
		//executeSu("echo 1250000 > /sys/devices/system/cpu/cpu0/cpufreq/scaling_setspeed");
		return data.getMaxFreq();
	}


	//decrease CPU by a certain increment
	//this will be at certain intervals until use is impatient
	public static void decreaseFreq(){
		String curr_freq = getCurrentFreq();
		String next_freq =  data.nextFreq(curr_freq);
		Log.d(TAG, "echo " + next_freq + " > /sys/devices/system/cpu/cpu0/cpufreq/scaling_setspeed");
		executeSu("echo " + next_freq + " > /sys/devices/system/cpu/cpu0/cpufreq/scaling_setspeed");
	}



	//find fraction needed to set frequency to unavailable frequency
	//desired is in kHz!!!!
	public static String[] calculateFreq(int desiredFreq)
	{
		/* w = desired frequency
		 * x = fraction needed
		 * y = upper frequency
		 * z = lower frequency
		 * 
		 * x = (w - z) / (y - z)
		 * 
		 * ex given w = 500, y = 600, z = 200
		 * x = (500 - 200) / (600 - 200) = 300 / 400 = 3 / 4
		 */

		String retValues[] = new String[3]; //return fraction, upper, lower
		int upper = 0;
		int lower = 0;
		double timeFraction = 0;
		//DecimalFormat df = new DecimalFormat("##.###");
		boolean found = false; //flag for finding an available freq = to desired freq

		//get freqs, convert to int array
		String freqList[] = getAvailableFreq().split(" ");
		int available[] = new int[freqList.length];
		for (int i = 0; i < freqList.length; i++)
		{
			available[i] = Integer.parseInt(freqList[i]);
		}

		//find upper and lower
		for (int i = 0; i < available.length; i++)
		{
			if (available[i] == desiredFreq)
			{
				upper = desiredFreq;
				lower = desiredFreq;
				found = true;
				break;
			}
			else if (available[i] > desiredFreq)
			{
				upper = available[i];
			}
			else if (available[i] < desiredFreq)
			{
				lower = available[i];
				break;
			}
		}

		//calculate fraction
		if(found)
		{
			timeFraction = 1;
		}
		else
		{
			timeFraction = (desiredFreq - lower) / (double)(upper - lower);
		}
		retValues[0] = Double.toString(timeFraction);
		retValues[1] = Integer.toString(upper);
		retValues[2] = Integer.toString(lower);
		Log.i(TAG, retValues[0]);
		Log.i(TAG, retValues[1]);
		Log.i(TAG, retValues[2]);

		return retValues;

	}


	//this stores the average CPU freq that the user gets impatient at
	private void storeUserImpatience(String curr_frequency){

	}


	//gets the current running process
	private int getActiveProcess(){
		/*RunningAppProcessInfo app = new RunningAppProcessInfo();
		 int pid = app.pid;
		 return pid;*/
		return 2;
	}


	//executes a command needing su
	private static void executeSu(String command)
	{
		Process process;
		DataOutputStream os;

		try {
			//get root access
			process = Runtime.getRuntime().exec("su");

			//set up stream to process
			os = new DataOutputStream(process.getOutputStream());

			//send command to process
			os.writeBytes(command + "\n");
			os.flush();

			//exit process
			os.writeBytes("exit\n");
			os.flush();

			//close stream & process
			os.close();

		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}   
	}


	//reads the file at a given location
	private static String executeRead(String location)
	{
		ProcessBuilder cmd;
		StringBuilder sb = null;

		try{
			String[] args = {"/system/bin/cat", location};
			cmd = new ProcessBuilder(args);

			Process process = cmd.start();
			InputStream in = process.getInputStream();

			sb = new StringBuilder();
			Scanner sc = new Scanner(in);
			while(sc.hasNextLine()){
				sb.append(sc.nextLine());
			}

			in.close();
		} catch(IOException ex){
			ex.printStackTrace();
		}
		return sb.toString();
	}








	//////////////////////////////////////////////////////////////////
	//	inner class that holds the persistent data					//
	//////////////////////////////////////////////////////////////////
	public class Data {
		ArrayList<String> avail_freqs;

		public Data(){
			avail_freqs = getAvailableFreqs();
		}

		public String nextFreq(String curr_freq){
			if(avail_freqs.indexOf(curr_freq) < avail_freqs.size() - 1){
				Log.d(TAG, avail_freqs.get(avail_freqs.indexOf(curr_freq) + 1));
				return avail_freqs.get(avail_freqs.indexOf(curr_freq) + 1);
			}
			else{
				Log.d(TAG, curr_freq);
				return curr_freq;
			}
		}

		public String getMaxFreq(){
			return avail_freqs.get(0);
		}

		//public void impatient(){
		//	curr_freq = avail_freqs.size() - 1;
		//}


		//return available frequencies
		public ArrayList<String> getAvailableFreqs()
		{
			String freq_string = executeRead("/sys/devices/system/cpu/cpu0/cpufreq/scaling_available_frequencies");
			ArrayList<String> freq_array = new ArrayList<String>();

			Scanner tokens = new Scanner(freq_string);
			while(tokens.hasNext()){
				freq_array.add(tokens.next());
			}

			return freq_array;
		}
	}



}
